package net.es.lookup.scalability.testing;

import client_tests.ResultTransmitter;
import com.jcraft.jsch.*;

import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.io.InputStream;

/**
 * Created with IntelliJ IDEA.
 * User: student5
 * Date: 8/7/13
 * Time: 10:35 AM
 * To change this template use File | Settings | File Templates.
 */
public class MessengerThread extends Thread {

    private String workingDir;
    private String resultRepo;
    private String fileLoc;
    private String fileName;
    private final String STATUS_OKAY = "okay";
    private final String STATUS_ERROR = "error";
    private String host;
    private Integer port;
    private String user;
    private String password;

    private String status;
    private Channel channel;
    private StringBuilder outBuffer;
    private StringBuilder errBuffer;
    private Session session;
    private JSch jsch = new JSch();
    private InputStream outStream;
    private InputStream errStream;
    private String output;
    private String error;
    private OutputWriter outputWriter;

    private Logger LOG = new Logger() { //TODO: change this
        @Override
        public boolean isEnabled(int i) {
            return true;
        }

        @Override
        public void log(int i, String s) {
            if (i > 4) {
                System.out.println(i + ", " + s);
            }
        }
    };


    public MessengerThread(String user, java.util.List<String> host, String workingDir, String resultRepo, String fileLoc, String sshKeyFile, String password) {

        this.user = user;
        this.host = host.get(0);
        this.password = password;
        this.port = Integer.decode(host.get(1));
        this.workingDir = workingDir + ((workingDir.endsWith("/")) ? "" : "/");
        this.resultRepo = resultRepo;
        this.fileLoc = fileLoc;
        this.fileName = fileLoc.substring(fileLoc.lastIndexOf("/") + 1);
        this.outputWriter = new OutputWriter();

        try {
            outBuffer = new StringBuilder();
            errBuffer = new StringBuilder();

            JSch.setLogger(LOG);

            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no");

            if (sshKeyFile != null && !sshKeyFile.isEmpty()) { //TODO: implement public key vs password function

                config.put("PreferredAuthentications", "publickey");
                jsch.addIdentity(sshKeyFile, password);
            }

            session=jsch.getSession(this.user, this.host, this.port);
            session.setConfig(config);

            UserInfo ui = new MyUserInfo();
            session.setUserInfo(ui);
            session.connect();

            status = STATUS_OKAY;
            System.out.println("Session status with " + host + " is: " + status);
        } catch (JSchException e) {

            System.out.println("Session failed to connect");
            e.printStackTrace();
            status = STATUS_ERROR;
            Coordinator.setShutdownFlag(true);
        }
    }

    public void run() {

        try {

            System.out.println("copying program to host");
            status = scpToHost();
            /*System.out.println("compiling program");
            status = compile();*/

            //TODO: verify successful compilation

            System.out.println("running program");
            status = runFile();

            Coordinator.decrementPending();

        } catch (Exception e) {

            e.printStackTrace();
            synchronized (this) {

                Coordinator.setShutdownFlag(true);
                this.notifyAll();
            }
            return;
        }
    }

    public boolean isConnected() {

        return session.isConnected();
    }

    private String scpToHost() throws Exception {

        String command = "scp " + fileLoc + " " + user + "@" + host + ":" + workingDir;
        System.out.println(command);
        verifyStatus();

        Runtime rt = Runtime.getRuntime();
        Process p = rt.exec(command);
        p.waitFor();

        System.out.println("scp result value = " + p.exitValue());

        return (p.exitValue() == 0) ? STATUS_OKAY : STATUS_ERROR;
    }

    private String compile() throws Exception {

        String command = "cd " + workingDir + " ; javac " + fileName;
        System.out.println(command);
        verifyStatus();

        return exec(command);
    }

    private String runFile() throws Exception {

        String command = "cd " + workingDir + " ; java" + (fileName.endsWith(".jar") ? (" -jar " + fileName) : (" " + fileName.substring(0, fileName.lastIndexOf(".")))) + " " + host + " " + Configuration.slsServer + " " + Configuration.port + " multiple-regs 100 2 100";
        verifyStatus();
        //command = "pwd";
        System.out.println(command);
        return exec(command);
    }

    private String exec(String command) throws JSchException, IOException {

        String status = null;

        channel = session.openChannel("exec");
        ((ChannelExec) channel).setCommand(command);
        channel.connect();

        outStream = channel.getInputStream();
        errStream = ((ChannelExec) channel).getErrStream();

        OutStreamReader osr = new OutStreamReader();
        osr.run();

        int readByte = errStream.read();

        while (readByte != 0xffffffff) {

            errBuffer.append((char) readByte);
            readByte = errStream.read();
        }

        output = outBuffer.toString();
        error = errBuffer.toString();
        if (error.isEmpty()) {
            status = STATUS_OKAY;
        } else {
            status = STATUS_ERROR;
            //TODO: exit here, because there was a comm error (or retry)
        }

        OutputWriter.add(error);
        OutputWriter.add(output);

        System.out.println("error stream = " + error);
        System.out.println("output stream = " + output);

        return status;
    }

    private void checkSession() {

        if (!session.isConnected()) {

            try {

                session.connect();
            } catch (JSchException e) {

                status = STATUS_ERROR;
                error = e.getMessage();
            }
        }
    }

    private void verifyStatus() throws Exception {

        if (!status.equals(STATUS_OKAY)) {

            throw new Exception();
        }
    }

    public void disconnect() {

        channel.disconnect();
        session.disconnect();
    }

    private class OutStreamReader implements Runnable {

        @Override
        public void run() {

            int readByte = 0;
            try {
                readByte = outStream.read();

                while (readByte != 0xffffffff) {

                    outBuffer.append((char) readByte);
                    readByte = outStream.read();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private class MyUserInfo implements UserInfo, UIKeyboardInteractive {
        public String getPassword() {
            return password;
        }

        public boolean promptYesNo(String str) {
            Object[] options = { "yes", "no" };
            int foo = JOptionPane.showOptionDialog(null, str, "Warning",
                                                          JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
                                                          null, options, options[0]);
            return foo == 0;
        }

        String passwd;
        JTextField passwordField = (JTextField) new JPasswordField(20);

        public String getPassphrase() {
            return null;
        }

        public boolean promptPassphrase(String message) {
            return true;
        }

        public boolean promptPassword(String message) {
            /*Object[] ob = { passwordField };
            int result = JOptionPane.showConfirmDialog(null, ob, message,
                    JOptionPane.OK_CANCEL_OPTION);
            if (result == JOptionPane.OK_OPTION) {
                passwd = passwordField.getText();
                return true;
            } else {
                return false;
            }*/
            return true;
        }

        public void showMessage(String message) {
            JOptionPane.showMessageDialog(null, message);
        }

        final GridBagConstraints gbc = new GridBagConstraints(0, 0, 1, 1, 1, 1,
                                                                     GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
                                                                     new Insets(0, 0, 0, 0), 0, 0);
        private Container panel;

        public String[] promptKeyboardInteractive(String destination,
                                                  String name, String instruction, String[] prompt, boolean[] echo) {
            panel = new JPanel();
            panel.setLayout(new GridBagLayout());

            gbc.weightx = 1.0;
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.gridx = 0;
            panel.add(new JLabel(instruction), gbc);
            gbc.gridy++;

            gbc.gridwidth = GridBagConstraints.RELATIVE;

            JTextField[] texts = new JTextField[prompt.length];
            for (int i = 0; i < prompt.length; i++) {
                gbc.fill = GridBagConstraints.NONE;
                gbc.gridx = 0;
                gbc.weightx = 1;
                panel.add(new JLabel(prompt[i]), gbc);

                gbc.gridx = 1;
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.weighty = 1;
                if (echo[i]) {
                    texts[i] = new JTextField(20);
                } else {
                    texts[i] = new JPasswordField(20);
                }
                panel.add(texts[i], gbc);
                gbc.gridy++;
            }

            if (JOptionPane.showConfirmDialog(null, panel, destination + ": "
                                                                   + name, JOptionPane.OK_CANCEL_OPTION,
                                                     JOptionPane.QUESTION_MESSAGE) == JOptionPane.OK_OPTION) {
                String[] response = new String[prompt.length];
                for (int i = 0; i < prompt.length; i++) {
                    response[i] = texts[i].getText();
                }
                return response;
            } else {
                return null; // cancel
            }
        }
    }
}
